
package br.uefs.ecomp.redeneural.model;

import br.uefs.ecomp.redeneural.model.io.VetorEntrada;
import br.uefs.ecomp.redeneural.util.Constantes;

/**
 *
 * @author Beto
 */
public class Algoritmos {

    public static double[] forward(Rede rede, VetorEntrada vetorEntrada) {
        int qtdNeuronios;
        double entrada[], saida[];
                
        // ***** Camada de Entrada ***** //
        Camada camadaEntrada = rede.getCamadas().get(0);
        qtdNeuronios = camadaEntrada.getNeuronios().size();
        saida = new double[qtdNeuronios];        
        
        for(int i = 0; i < qtdNeuronios; i++) {
            Neuronio _neuronio = camadaEntrada.getNeuronios().get(i);
            _neuronio.setEntradas(vetorEntrada.getParametros());
            saida[i] = _neuronio.gerarSaida();            
        }        
        
        // ***** Próximas Camadas ***** //
        entrada = saida;
        
        for(int i = 1; i < rede.getCamadas().size(); i++) {
            Camada _camada = rede.getCamadas().get(i);
            qtdNeuronios = _camada.getNeuronios().size();
            saida = new double[qtdNeuronios];
            
            for(int j = 0; j < qtdNeuronios; j++) {
                Neuronio _neuronio = _camada.getNeuronios().get(j);
                _neuronio.setEntradas(entrada);
                saida[j] = _neuronio.gerarSaida();
            }
            
            entrada = saida;
        }

        return saida;
    }


    public static Rede backward(Rede rede, double saidaDesejada, double taxaAprendizagem) {

        // ***** Camada de Saída ***** //
        Camada camadaSaida = rede.getCamadas().lastElement();
        double[] pesosNovos, pesosAtuais;
        double pesoBiasNovo, gradienteLocal;

        for (int i = 0; i < camadaSaida.getNeuronios().size(); i++) {
            Neuronio neuronio = camadaSaida.getNeuronios().get(i);
            pesosNovos  = new double[neuronio.getPesos().length];
            pesosAtuais = neuronio.getPesos();
            gradienteLocal = Calculos.gradienteLocalUltimaCamada(saidaDesejada, neuronio.gerarSaida(), neuronio.somatorio());
            pesoBiasNovo = neuronio.getPesoBias() + taxaAprendizagem * gradienteLocal * Constantes.BIAS;
            
            for (int j = 0; j < pesosAtuais.length; j++) {
                pesosNovos[j] = pesosAtuais[j] + taxaAprendizagem * gradienteLocal * neuronio.getEntradas()[j];
            }

            neuronio.setUltimoGradienteLocal(gradienteLocal);
            neuronio.setPesoBias(pesoBiasNovo);
            neuronio.setPesos(pesosNovos);
            camadaSaida.setNeuronio(i, neuronio);
        }

        rede.setCamada(rede.getCamadas().size()-1, camadaSaida);


        // ***** Camadas Ocultas ***** //
        int aux = rede.getCamadas().size() - 2;

        for(int i = aux; i >= 0; i--) {
            Camada _camada = rede.getCamadas().get(i);
            Camada _camadaPosterior = rede.getCamadas().get(i+1);

            for (int j = 0; j < _camada.getNeuronios().size(); j++) {
                Neuronio neuronio = _camada.getNeuronios().get(i);
                pesosNovos  = new double[neuronio.getPesos().length];
                gradienteLocal = Calculos.gradienteLocalCamadasOcultas(neuronio, _camadaPosterior, j);
                pesoBiasNovo = neuronio.getPesoBias() + taxaAprendizagem * gradienteLocal * Constantes.BIAS;

                for(int k = 0; k < neuronio.getPesos().length; k++) {
                    pesosNovos[k] = neuronio.getPesos()[k] + taxaAprendizagem * gradienteLocal * neuronio.getEntradas()[k];
                }

                neuronio.setUltimoGradienteLocal(gradienteLocal);
                neuronio.setPesoBias(pesoBiasNovo);
                neuronio.setPesos(pesosNovos);
                _camada.setNeuronio(j, neuronio);
            }

            rede.setCamada(i, _camada);
        }

        return rede;
    }
}
